package com.zooltech.samples.img;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;

import javax.imageio.ImageIO;

/**
 * <strong>Title : SPT2PNG </strong>. <br>
 * <strong>Description : </strong>DOS时代的金山WPS中SPT图形格式转换为PNG格式图片.<br>
 * <br>SPT数据格式：<br>
 * 前64字节为文件头，后面是数据。<br>
 * 文件头前16个字节是"Super-Star File."<br>
 * 第34、35字节是图片宽度，第36、37字节是图片高度。<br>
 * 第39、40字节是是否压缩的标记。非压缩是01H,00H，压缩是05H,80H。<br>
 * SPT图形是黑白图形。坐标是从左上角开始，从左到右，从上倒下，每个点1bit。<br>
 * 非压缩数据：字节的每位对应图片左上角开始，从左到右从上倒下的点阵。<br>
 * 压缩数据格式是：控制字节，数据，...，数据；控制字节,数据，...，数据；...；控制字节,数据，...，数据<br>
 * 如果控制字节值≥128,则其后数据重复 (256-控制字节值+1) 次，否则，控制字节之后的 (控制字节值+1) 个字节都为数据。<br>
 * <br><strong>Create on : 2013-8-19 下午3:53:42 </strong>. <br>
 * @author zooltech zooltech@gmail.com <br>
 * @version 1.0<br>
 * <br>
 *          <strong>修改历史: .</strong> <br>
 *          修改人 修改日期 修改描述<br>
 *          -------------------------------------------<br>
 * <br>
 */
public class SPT2PNG {

	/**
	 *  字节蒙板.
	 */
	private static final int MASK = 128;

	/**
	 * 读取SPT文件
	 * @param file SPT文件名
	 * @return 图片数据
	 */
	public BufferedImage readSPT(String file) {
		File f = new File(file);
		return readSPT(f);
	}

	/**
	 * 读取SPT文件
	 *
	 * @param f SPT文件对象
	 * @return 图片数据
	 */
	public BufferedImage readSPT(File f) {
		BufferedImage img = null;
		try {
			InputStream is = new FileInputStream(f);
			byte[] sptHead = new byte[64];
			int res = is.read(sptHead);
			if (res != 64) {
				System.out.println("读取64位文件头出错。");
			}
			int width = (sptHead[35] & 0xff) * 256 + (sptHead[34] & 0xff);
			int height = (sptHead[37] & 0xff) * 256 + (sptHead[36] & 0xff);
			boolean isCompact = (sptHead[39] & 0x80) == 128;
			System.out.println(MessageFormat.format("图片尺寸 - 宽：{0}，高：{1}，压缩：{2}\n", width, height, isCompact));
			if (isCompact) {
				img = compactSpt(is, width, height);
			} else {
				img = normalspt(is, width, height);
			}

			return img;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 非压缩SPT处理.
	 * 
	 * @param is spt数据流
	 * @param width 图片宽度
	 * @param height 图片高度
	 * @return 图片数据
	 */
	private BufferedImage normalspt(InputStream is, int width, int height) {
		BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
		int byteWidth = width >> 3;
		byte[] dots = new byte[byteWidth];
		int color, y = 0;
		try {
			while (is.read(dots) != -1) {
				for (int i = 0; i < byteWidth; i++) {
					byte b = dots[i];
					for (int mask = MASK, j = 0; mask > 0; mask >>>= 1, j++) {
						if ((b & mask) != 0) {
							color = 0xffffff;
						} else {
							color = 0;
						}
						img.setRGB(i * 8 + j, y, color);
					}
				}
				y++;
			}
			return img;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 压缩SPT处理.
	 * 
	 * @param is spt数据流
	 * @param width 图片宽度
	 * @param height 图片高度
	 * @return 图片数据
	 */
	private BufferedImage compactSpt(InputStream is, int width, int height) {
		BufferedImage img = new BufferedImage(width, height + 1, BufferedImage.TYPE_INT_RGB);
		int data, d, color;
		// 像素指针
		long pos = 0;

		try {
			while ((data = is.read()) != -1) {
				if (data < 128) {
					for (int i = 0; i <= data; i++) {
						d = is.read();
						for (int mask = MASK; mask > 0; mask >>>= 1) {
							if ((d & mask) != 0) {
								color = 0xffffff;
							} else {
								color = 0;
							}
							img.setRGB((int) (pos % width),
									(int) (pos / width), color);
							pos++;
						}
					}
				} else {
					d = is.read();
					int[] bs = new int[8];
					for (int mask = MASK, j = 0; mask > 0; mask >>>= 1, j++) {
						if ((d & mask) != 0) {
							bs[j] = 0xffffff;
						} else {
							bs[j] = 0;
						}
					}
					for (int i = 0, count = 256 - data; i <= count; i++) {
						for (int j = 0; j < 8; j++) {
							img.setRGB((int) (pos % width),
									(int) (pos / width), bs[j]);
							pos++;
						}
					}
				}
				//防止越界。SPT文件正常情况下不会发生。
				if (pos / width >= height) break;
			}
			return img;
		} catch (Exception e) {
			System.out.println((int) (pos % width) + "," + (int) (pos / width));
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * @param f 要进行转缓的目录或文件.
	 */
	private void doTrans(File f) {
		if (!f.exists()) {
			System.out.println("目录或文件不存在。");
			return;
		}
		if (f.isDirectory()) {
			File[] sub = f.listFiles();
			if (sub == null || sub.length < 1) {
				return;
			}
			for (int i = 0; i < sub.length; i++) {
				doTrans(sub[i]);
			}
		} else if (f.isFile()) {
			String path = f.getAbsolutePath();
			if (path.toLowerCase().endsWith(".spt")) {
				System.out.print(path);
				path = path.substring(0, path.lastIndexOf('.')).concat(".png");
				System.out.println(" ==> " + path);
				BufferedImage img = readSPT(f);
				try {
					ImageIO.write(img, "PNG", new File(path));
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 将指定目录及其子目录中所有金山SPT图形文件转为PNG图形文件.
	 * @param args 命令行参数
	 */
	public static void main(String[] args) {
		if (args == null || args.length < 1) {
			System.out .println("将指定目录及其子目录中所有金山SPT图形文件转为PNG图形文件。\n用法：java -jar SPT2PNG.jar <要转换的目录或文件>。");
			return;
		}
		new SPT2PNG().doTrans(new File(args[0]));
		System.out.println("完成");
	}
}
